#include "QiniuUploader.h"

QiniuUploader::QiniuUploader(QObject *parent)
    : QObject(parent)
    , m_networkManager(new QNetworkAccessManager(this))
    , m_region("z2") // 默认为华南区域
{
}

QiniuUploader::~QiniuUploader()
{
}

void QiniuUploader::setConfig(const QString &accessKey, const QString &secretKey,
                             const QString &bucket, const QString &region)
{
    m_accessKey = accessKey;
    m_secretKey = secretKey;
    m_bucket = bucket;
    m_region = region;
}

void QiniuUploader::uploadFile(const QString &filePath, const QString &customKey)
{
    // 检查文件是否存在
    QFileInfo fileInfo(filePath);
    if (!fileInfo.exists()) {
        emit uploadFailed("文件不存在: " + filePath);
        return;
    }

    // 确定上传的key（文件名）
    QString key = customKey.isEmpty() ? fileInfo.fileName() : customKey;

    // 生成上传凭证
    QString uploadToken = generateUploadToken(m_bucket, key);

    // 创建multipart请求
    QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);

    // 添加token字段
    QHttpPart tokenPart;
    tokenPart.setHeader(QNetworkRequest::ContentDispositionHeader,
                        QVariant("form-data; name=\"token\""));
    tokenPart.setBody(uploadToken.toUtf8());
    multiPart->append(tokenPart);

    // 添加key字段
    QHttpPart keyPart;
    keyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
                      QVariant("form-data; name=\"key\""));
    keyPart.setBody(key.toUtf8());
    multiPart->append(keyPart);

    // 添加文件内容
    QFile *file = new QFile(filePath);
    if (!file->open(QIODevice::ReadOnly)) {
        delete multiPart;
        delete file;
        emit uploadFailed("无法打开文件: " + filePath);
        return;
    }

    // 获取文件的MIME类型
    QMimeDatabase mimeDB;
    QString mimeType = mimeDB.mimeTypeForFile(filePath).name();

    QHttpPart filePart;
    filePart.setHeader(QNetworkRequest::ContentDispositionHeader,
                       QVariant("form-data; name=\"file\"; filename=\"" + key + "\""));
    filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(mimeType));
    filePart.setBodyDevice(file);
    file->setParent(multiPart); // 确保文件在multiPart被删除时也被删除
    multiPart->append(filePart);

    // 创建网络请求
    QNetworkRequest request(QUrl(this->getUploadDomain()));

    // 发送请求
    QNetworkReply *reply = m_networkManager->post(request, multiPart);
    multiPart->setParent(reply); // 确保multiPart在reply完成时被删除

    // 监听上传进度
    connect(reply, &QNetworkReply::uploadProgress, this, [this](qint64 bytesSent, qint64 bytesTotal) {
        if (bytesTotal > 0) {
            int percent = static_cast<int>((bytesSent * 100) / bytesTotal);
            emit uploadProgress(percent);
        }
    });

    // 处理请求完成
    connect(reply, &QNetworkReply::finished, this, [this, reply, key]() {
        reply->deleteLater();

        if (reply->error() == QNetworkReply::NoError) {
            // 解析返回的JSON数据
            QByteArray responseData = reply->readAll();
            QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
            QJsonObject jsonObj = jsonDoc.object();

            if (jsonObj.contains("key")) {
                // 构建文件URL
                QString fileUrl = QString("http://%1.%2/%3")
                                  .arg(m_bucket)
                                  .arg(getUploadDomain().mid(7)) // 去掉"http://"前缀
                                  .arg(key);

                emit uploadSuccess(fileUrl, key);
            } else {
                emit uploadFailed("上传成功但返回数据格式异常: " + QString(responseData));
            }
        } else {
            emit uploadFailed("上传失败: " + reply->errorString());
        }
    });
}

QString QiniuUploader::generateUploadToken(const QString &bucket, const QString &key)
{
    // 构建上传策略
    QJsonObject policy;
    policy["scope"] = bucket + ":" + key;

    // 设置截止时间，默认1小时后过期
    uint deadline = QDateTime::currentDateTimeUtc().toTime_t() + 3600;
    policy["deadline"] = static_cast<qint64>(deadline);

    // 策略转为JSON字符串，再进行Base64编令
    QJsonDocument doc(policy);
    QByteArray policyJson = doc.toJson(QJsonDocument::Compact);
    QByteArray encodedPolicy = policyJson.toBase64();
    encodedPolicy = encodedPolicy.replace('+', '-').replace('/', '_');

    // 使用SecretKey对编码后的上传策略签名
    QByteArray sign = hmacSha1(m_secretKey.toUtf8(), encodedPolicy);
    sign = sign.toBase64();
    sign = sign.replace('+', '-').replace('/', '_');

    // 拼接上传凭证
    return m_accessKey + ":" + sign + ":" + encodedPolicy;
}

QByteArray QiniuUploader::hmacSha1(const QByteArray &key, const QByteArray &data)
{
    QByteArray innerPadding(64, char(0x36));
    QByteArray outerPadding(64, char(0x5c));

    // 准备密钥
    QByteArray keyBytes = key;
    if (keyBytes.length() > 64) {
        QCryptographicHash hash(QCryptographicHash::Sha1);
        hash.addData(keyBytes);
        keyBytes = hash.result();
    }

    // 应用XOR操作
    for (int i = 0; i < keyBytes.length(); i++) {
        innerPadding[i] = innerPadding[i] ^ keyBytes[i];
        outerPadding[i] = outerPadding[i] ^ keyBytes[i];
    }

    // 计算内部哈希
    QCryptographicHash innerHash(QCryptographicHash::Sha1);
    innerHash.addData(innerPadding);
    innerHash.addData(data);
    QByteArray innerResult = innerHash.result();

    // 计算外部哈希
    QCryptographicHash outerHash(QCryptographicHash::Sha1);
    outerHash.addData(outerPadding);
    outerHash.addData(innerResult);

    return outerHash.result();
}

QString QiniuUploader::getUploadDomain() const
{
    // 根据区域代码返回对应的上传域名
    if (m_region == "z0") {
        return "http://upload.qiniup.com"; // 华东区域
    } else if (m_region == "z1") {
        return "http://upload-z1.qiniup.com"; // 华北区域
    } else if (m_region == "z2") {
        return "http://upload-z2.qiniup.com"; // 华南区域
    } else if (m_region == "na0") {
        return "http://upload-na0.qiniup.com"; // 北美区域
    } else if (m_region == "as0") {
        return "http://upload-as0.qiniup.com"; // 亚太区域
    } else {
        return "http://upload.qiniup.com"; // 默认使用华东区域
    }
}
